package com.zwps.common.controller.swagger;

import static org.springdoc.core.SpringDocAnnotationsUtils.extractSchema;
import static org.springdoc.core.converters.ConverterUtils.isResponseTypeWrapper;

import java.lang.annotation.Annotation;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.List;

import org.apache.commons.lang3.ArrayUtils;
import org.springdoc.core.GenericResponseService;
import org.springdoc.core.OperationService;
import org.springdoc.core.PropertyResolverUtils;
import org.springdoc.core.ReturnTypeParser;
import org.springdoc.core.SpringDocAnnotationsUtils;
import org.springdoc.core.SpringDocConfigProperties;
import org.springframework.core.ResolvableType;

import com.fasterxml.jackson.annotation.JsonView;
import com.fasterxml.jackson.core.type.TypeReference;
import com.zwps.common.controller.response.message.ResponseDataMessage;
import com.zwps.common.controller.response.message.ResponseDataPageMessage;
import com.zwps.common.controller.response.message.ResponseMessage;

import cn.hutool.core.lang.ParameterizedTypeImpl;
import io.swagger.v3.oas.models.Components;

/**
 * 处理当接口没有显示声明响应包装类时，文档生成时自动加上
 * @ClassName: SwaggerResponseWrapper
 * @Description: 
 * @author lyh
 * @date 2023年7月30日 下午9:59:42
 *
 */
public class SwaggerResponseWrapper extends GenericResponseService {

    public SwaggerResponseWrapper(OperationService operationService, List<ReturnTypeParser> returnTypeParsers,
            SpringDocConfigProperties springDocConfigProperties, PropertyResolverUtils propertyResolverUtils) {
        super(operationService, returnTypeParsers, springDocConfigProperties, propertyResolverUtils);
    }

    public io.swagger.v3.oas.models.media.Content buildContent(Components components, Annotation[] annotations,
            String[] methodProduces, JsonView jsonView, Type returnType) {
        
        // if void, no content
        if (isVoid(returnType)) {
            returnType = ResponseMessage.class;
        }
        if (returnType == String.class) { // 全局封装的代码如果接口返回字符串表示不进行封装基础响应信息
            return null;
        }
        Class<?>[] fileterClass = { ResponseMessage.class, ResponseDataMessage.class, ResponseDataPageMessage.class };
        boolean filter = false;
        for (Class<?> classz : fileterClass) {
            if (returnType.getTypeName().startsWith(classz.getTypeName())) {
                filter = true;
                break;
            }
        }
        if (!filter) {
            final Type[] types = new Type[] { returnType };
            TypeReference<ResponseDataMessage<?>> typeReference = new TypeReference<ResponseDataMessage<?>>() {
                @Override
                public Type getType() {
                    return new ParameterizedTypeImpl(types, ResponseDataMessage.class, ResponseDataMessage.class);
                }
            };
            returnType = typeReference.getType();
        }

        io.swagger.v3.oas.models.media.Content content = new io.swagger.v3.oas.models.media.Content();
        if (ArrayUtils.isNotEmpty(methodProduces)) {
            io.swagger.v3.oas.models.media.Schema<?> schemaN = calculateSchema(components, returnType, jsonView,
                    annotations);
            if (schemaN != null) {
                io.swagger.v3.oas.models.media.MediaType mediaType = new io.swagger.v3.oas.models.media.MediaType();
                mediaType.setSchema(schemaN);
                // Fill the content
                Arrays.stream(methodProduces).forEach(mediaTypeStr -> content.addMediaType(mediaTypeStr, mediaType));
            }
        }
        return content;
    }

    /**
     * Calculate schema schema.
     *
     * @param components  the components
     * @param returnType  the return type
     * @param jsonView    the json view
     * @param annotations the annotations
     * @return the schema
     */
    private io.swagger.v3.oas.models.media.Schema<?> calculateSchema(Components components, Type returnType,
            JsonView jsonView, Annotation[] annotations) {
        if (!isVoid(returnType) && !SpringDocAnnotationsUtils.isAnnotationToIgnore(returnType))
            return extractSchema(components, returnType, jsonView, annotations);
        return null;
    }

    private boolean isVoid(Type returnType) {
        boolean result = false;
        if (Void.TYPE.equals(returnType) || Void.class.equals(returnType))
            result = true;
        else if (returnType instanceof ParameterizedType) {
            Type[] types = ((ParameterizedType) returnType).getActualTypeArguments();
            if (types != null && isResponseTypeWrapper(ResolvableType.forType(returnType).getRawClass()))
                result = isVoid(types[0]);
        }
        return result;
    }

}
